/*
* Copyright (c) 2016 ShopJsp. All Rights Reserved.
 * ============================================================================
 * 版权所有 2011 - 今 北京华宇盈通科技有限公司，并保留所有权利。
 * ----------------------------------------------------------------------------
 * 提示：在未取得SHOPJSP商业授权之前，您不能将本软件应用于商业用途，否则SHOPJSP将保留追究的权力。
 * ----------------------------------------------------------------------------
 * 官方网站：http://www.shopjsp.com
 * ============================================================================
*/
package com.hyyt.shopjsp.shiro.service;


import com.hyyt.shopjsp.basic.pojo.BasicActor;
import com.hyyt.shopjsp.basic.pojo.BasicPurview;
import com.hyyt.shopjsp.basic.pojo.BasicUsers;
import com.hyyt.shopjsp.basic.service.IBasicActorService;
import com.hyyt.shopjsp.basic.service.IBasicUsersService;
import com.hyyt.shopjsp.common.utils.PurviewUtil;
import com.hyyt.shopjsp.util.common.LoggerUtils;
import com.hyyt.shopjsp.util.common.StringStaticKey;
import com.hyyt.shopjsp.util.common.Utils;
import com.hyyt.shopjsp.util.json.FastJsonUtils;
import com.hyyt.shopjsp.util.redis.service.IRedisBasicService;
import com.hyyt.shopjsp.util.redis.util.RedisDataReadUtil;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Copyright (c) 2016 ShopJsp. All Rights Reserved.
 * ============================================================================
 * 版权所有 2011 - 今 北京华宇盈通科技有限公司，并保留所有权利。
 * ----------------------------------------------------------------------------
 * 提示：在未取得SHOPJSP商业授权之前，您不能将本软件应用于商业用途，否则SHOPJSP将保留追究的权力。
 * ----------------------------------------------------------------------------
 * 官方网站：http://www.shopjsp.com
 * ============================================================================
 * 核心权限授权验证
 * @author CuiYS on 2016/10/27 0027上午 11:31.
 */
@Service
public class UserRealm extends AuthorizingRealm {
    /**
     * 获取用户基本操作接口
     */
    @Resource
    private IBasicUsersService basicUsersService;
    @Resource
    private IBasicActorService basicActorService;
    @Resource
    private IRedisBasicService redisBasicService;

    public UserRealm() {
        super();
    }

    /**
     * 权限验证
     *      如果实现自动分配，使用该代码可以进行调取：UserRealm.doGetAuthorizationInfo(PrincipalCollection principalCollection)方法验证授权
     *      // Subject currentUser = SecurityUtils.getSubject();
     *      // boolean bl = currentUser.isPermitted(userName);
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        //权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取登录时输入的用户名，方式一
        //Object obj_temp = (Object) SecurityUtils.getSubject().getPrincipal();
        //获取登录时输入的用户名，方式二
        Object obj = principalCollection.fromRealm(getName()).iterator().next();
        BasicUsers users = null;
        if(null != obj && obj instanceof BasicUsers){
            users = (BasicUsers) obj;
        }
        if(Utils.objectIsNotEmpty(users)) {
            //登录管理员名称
            String loginName = users.getUserName();
            LoggerUtils.info(this.getClass(), "准备为登录的管理员：[" + loginName + "]授权");
            //定义用户的所有角色具备的权限
            List<BasicPurview> basicPurviews = new ArrayList<>();
            //根据登录用户获取存放redis中用户权限
            String purview = RedisDataReadUtil.getRedisValue(StringStaticKey.PURVIEW_USER_PREFIX + loginName);
            //如果用户权限不为空，则直接授权到
            if (Utils.objectIsNotEmpty(purview)) {
                //定义用户的所有角色具备的权限
                basicPurviews = FastJsonUtils.toList(purview, BasicPurview.class);
                //用户的角色对应的所有权限
                Set<String> setPurviews = PurviewUtil.getPermissionsFunctionSet(basicPurviews);
                //用户的角色对应的所有权限
                info.setStringPermissions(setPurviews);
                return info;
            } else {
                //查询出用户的所有角色
                List<BasicActor> basicActors = basicActorService.queryAllBasicActorByUsersId(users.getUsersId());
                if (Utils.objectIsNotEmpty(basicActors) && basicActors.size() > 0) {
                    for (BasicActor actors : basicActors) {
                        basicPurviews.addAll(actors.getBasicPurviews());
                    }
                    //当用户拥有2以上角色时，需要排除用户角色所拥有的重复权限，并且保持顺序
                    if(basicActors.size() > 1) {
                        PurviewUtil.removeDuplicateWithOrder(basicPurviews);
                    }
                    //存放redis中用户权限
                    redisBasicService.setKey(StringStaticKey.PURVIEW_USER_PREFIX + loginName, FastJsonUtils.toJSONString(basicPurviews));
                    //用户的角色集合
                    info.setRoles(PurviewUtil.getActorIdSet(basicActors));
                    //用户的角色对应的所有权限，本项目使用的是权限定义。如果只使用角色定义访问权限，则for方法可以注释
                    for (BasicActor actor : basicActors) {
                        info.addStringPermissions(PurviewUtil.getPermissionsUrlSet(actor.getBasicPurviews()));
                    }
                }
                return info;
            }
        } else {
            return info;
        }

    }

    /**
     * 登录认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;                    //获取基于用户名和密码的令牌
        //LoggerUtils.info(this.getClass(), "验证当前Subject时获取到token为：" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
        String loginName = token.getUsername();                                                        //获取登录用
        BasicUsers users = basicUsersService.getBasicUsersByUserName(loginName);                   //从数据库中获取对应用户名对应的user
        //定义用户的所有角色具备的权限
        List<BasicPurview> basicPurviews = new ArrayList<>();
        //数据库查询用户
        if (Utils.objectIsNotEmpty(users)) {
            //查询出用户的所有角色
            List<BasicActor> basicActors = basicActorService.queryAllBasicActorByUsersId(users.getUsersId());
            if (Utils.objectIsNotEmpty(basicActors) && basicActors.size() > 0) {
                basicPurviews = new ArrayList<>();
                for (BasicActor actors : basicActors) {
                    basicPurviews.addAll(actors.getBasicPurviews());
                }
                //当用户拥有2以上角色时，需要排除用户角色所拥有的重复权限，并且保持顺序
                if(basicActors.size() > 1) {
                    PurviewUtil.removeDuplicateWithOrder(basicPurviews);
                }
                //存放redis中用户权限
                redisBasicService.setKey(StringStaticKey.PURVIEW_USER_PREFIX + loginName, FastJsonUtils.toJSONString(basicPurviews));
            }
        }else{
            throw new AccountException("帐号或密码不正确！");                       //异常处理
        }
        return new SimpleAuthenticationInfo(users, users.getPassword(), getName());            //返回验证结果
    }

    /**
     * 自定义 shiro:hasPermission 标签支持 or 关键词
     * @param principals
     * @param permission
     * @return
     */
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        final String OR_OPERATOR = " or ";
        if(permission.contains(OR_OPERATOR)) {
            String[] permissions = permission.split(OR_OPERATOR);
            for(String orPermission : permissions) {
                if(super.isPermitted(principals, orPermission)) {
                    return true;
                }
            }
            return false;
        } else {
            return super.isPermitted(principals, permission);
        }
    }

    /**
     * 清空当前用户权限信息
     */
    public void clearCachedAuthorizationInfo() {
        PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 指定principalCollection 清除
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) {
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
        super.clearCachedAuthorizationInfo(principals);
    }

}
